
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <title>NES游戏开发</title>
    <link rel="stylesheet" href="index.css"/>
</head>

<body>

<div class="main">
<p><h1>cc65如何工作</h1>
</p><p>
</p><p>所有NES汇编程序都是命令行程序。那是什么意思？它没有图形用户界面。您不需要在其中键入代码。您必须在单独的程序（Notepad ++）中编写代码并保存。然后，打开命令提示符，并通过在命令提示符下键入来运行汇编程序。
</p><p>
</p><p>Cc65要复杂得多，它需要输入几个指令，有多个步骤，所以我们不打算使用命令提示符。但不要担心！我将为你简化它...我们将编写一个批处理文件（compile.bat）来自动化该过程。一旦编写完成，你所要做的就是双击compile.bat，如果一切顺利，它将生成你的NES文件。（用Notepad ++编写.bat文件。）
</p><p>
</p><p>如何cc65编译..首先，您将用C语言编写源代码（使用Notepad ++）。cc65将其编译为6502汇编代码。然后ca65将它组装成一个目标文件。然后ld65将您的目标文件（使用配置文件.cfg）链接到一个完整的.nes文件中。所有这些步骤都将写入批处理文件中，因此最后，您将双击.bat文件以执行所有这些步骤。
</p><p>
</p><p>我将所有源文件“#include”到一个.c文件中，并将“.include”或“.incbin”文件放在crt0.s.中。您唯一需要在.bat文件中更改的内容是主.c文件的“名称”。如果它们发生变化，你只需要更改（name）.c和crt0.s中包含文件的名称。
</p><p>
</p><p>更多关于cc65 ......
</p><p>
</p><p>运行NES的6502处理器是一个8位系统。它没有简单的方法来访问大于8位的变量，因此对大多数变量首选'unsigned char'。地址是16位，但几乎所有其他地方都处理8位。并且，它所知道的唯一数学是加法，减法和位移乘法/除法（乘法的 x2 等同于 << 1）。
</p><p>
</p><p>这意味着，由于系统的限制，您可能需要以非常规方式编写C代码。
</p><p><pre>
</p><p>1.变量应定义为 unsigned char（8位，范围值0-255）。
</p><p>2.使用全局变量（或本地静态变量）
</p><p>3.使用无参函数......
</p><p>造成C运行慢的原因是堆栈的存取操作。
</p><p>局部变量和传递的变量使用C堆栈，速度可能慢5倍。
</p><p>函数参数的替代方法是在函数调用之前将值存储到临时全局变量。
</p><p>这种情况很容易引起冲突，因此您可能希望立即将它们放入函数顶部的静态局部变量（也很快）。
</p><p>4.理想情况下，数组最多使用256 bytes
</p><p>5.像数组一样使用指针。用ptr[1] 代替 *(ptr+1)
</p><p>6.每行代码只放一个数组，解释......
</p><p>正确用法
</p><p>temp = array2[y];
</p><p>array1[x] = temp;
</p><p>错误用法
</p><p>array1[x] = array2[y]; 
</p><p>如果这些数组是小于256的全局字符数组，并且如果一行中只有一个，则可以将其转换为非常快的代码。
</p><p>如果你把2放在一行上，它会将右数组的地址传递给一个临时指针，
</p><p>用y做一些16位数学运算，然后它必须对第二个数组执行相同的操作，这需要大约5倍的时间。
</p><p>7.使用++g而不是g++
</p><p>cc65使用 “inc g” 来编译 ++g，g++ 使用的是 “lda g, clc, adc #1, sta g” 会多花费4倍的时间
</p><p>正确用法
</p><p>z = g;
</p><p>++g;
</p><p>错误用法
</p><p>z = g++;
</p><p>8. cc65不能按值传递结构，也不能返回结构。
</p><p>9.不要使用结构数组，使用带有数组的结构。
</p><p></pre>
</p><p>
</p><p>编译时，使用-O指令优化代码。还有i，r，s指令，它们有时组合为-Oirs，可以增加优化效果。但是，可能会导致异常（不过我没有遇到错误，所以我仍然使用-Oirs这个命令）。
</p><p>以下是cc65编码的更多建议......
</p><p><a href="http://www.cc65.org/doc/coding.html">http://www.cc65.org/doc/coding.html</a>
</p><p>
</p><p>为什么我们如此关注优化？因为时序非常重要，而且旧的8位处理器的资源非常薄。标准且整洁的C代码有可能会被编译成非常慢的代码，且占用有限的内存空间。
</p><p>
</p><p>在文件之间共享变量... asm模块（.s文件）可以使用import，export，importzp，exportzp定义彼此共享变量/标签。cc65可以通过声明变量“extern unsigned char foo;”来访问asm模块中的变量和数组（如果它是一个zeropage符号，则添加行#pragma zpsym（“foo”）;.当它编译C代码时，它将会为它添加一个导入定义。我几乎不使用“extern”。这可能是我提到它的唯一一次，除了可能......如果你有一个大的二进制文件...最容易在asm代码中“加入”它像这样......
</p><p>
</p><p>.export _foo
</p><p>_foo:
</p><p>.incbin “foo.bin”
</p><p>
</p><p>然后从C代码访问它，执行“extern unsigned char foo [];”。请注意下划线。出于某种原因，当cc65编译时，它会在每个符号前添加下划线。因此，在asm方面，您必须为每个导出的标签/变量添加下划线。
</p><p>您可以使用__fastcall__调用在ASM中编写的cc65中的函数。这将在A，X寄存器中存储最右侧传递的变量，而不是C堆栈。如果函数在C中，这没有用，因为它会立即将它传递给C堆栈以将其用作局部变量。
</p><p>
</p><p>当您传递变量时，会发生什么？
</p><p>（注意，测试是全局的）
</p><p>
</p><p><pre>
</p><p>Test(Foo);
</p><p>void Test (char A) {
</p><p>test = A;
</p><p>}
</p><p></pre>
</p><p>
</p><p>//一个传递的变量...编译成19行代码
</p><p>// 20，如果你在我们跳到这里之前计算'lda Foo'
</p><p>
</p><p><pre>
</p><p>_Test:
</p><p>
</p><p>jsr pusha
</p><p>ldy #$00
</p><p>lda (sp),y
</p><p>sta _test ; test = A;
</p><p>
</p><p>jmp incsp1
</p><p>
</p><p>pusha: ldy sp
</p><p>beq @L1
</p><p>dec sp
</p><p>ldy #0
</p><p>sta (sp),y
</p><p>rts
</p><p>
</p><p>@L1: dec sp+1
</p><p>dec sp
</p><p>sta (sp),y
</p><p>rts
</p><p>
</p><p>incsp1:
</p><p>
</p><p>inc sp
</p><p>bne @L1
</p><p>inc sp+1
</p><p>@L1: rts
</p><p></pre>
</p><p>
</p><p>没有传递变量，编译成3行代码
</p><p><pre>
</p><p>void Test（void）{ 
</p><p>test = A; 
</p><p>}
</p><p></pre>
</p><p><pre>
</p><p>_Test：
</p><p>lda _A 
</p><p>sta _test 
</p><p>rts
</p><p></pre>
</p><p>也可以将asm代码内联到C代码中。它看起来像这样......
</p><p><pre>
</p><p>asm (“Z: bit $2002”) ;
</p><p>asm (“bpl Z”) ;
</p><p></pre>
</p><p>另一个说明。我正在使用cc65附带的标准nes.lib文件。
</p><p>其他人一直在使用runtime.lib，如果你使用其他版本的cc65，它会无法使用。
</p><p>我增加了命令参数
</p><p><pre>
</p><p>--add-source
</p><p></pre>
</p><p>到命令行，编译代码时将生成一个包含c代码的.s（asm）文件，可以查看asm的代码。还会生成一个labels.txt文件，用于调试。
</p><p>
</p><p>
</p></div>
<script>
var h1 = document.getElementsByTagName("h1");
if(h1.length>0){
    document.title = h1[0].innerHTML;
}
</script>
</body>
</html>
